package com.letv.core.download.image;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.text.TextUtils;

import com.letv.core.download.image.ImageDownloader.DownloadRequest;

/**
 * 图片下载队列
 * 
 * @author zhuqiao
 *
 */
public class ImageRequestQueue {
    /** 子线程数量 **/
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 5;
    private static final int DEFAULT_CACHE_THREAD_POOL_SIZE = 2;

    /** 给请求生成自增序列号 **/
    private AtomicInteger mSequenceGenerator = new AtomicInteger();

    private final Set<DownloadRequest> mCurrentRequests = new HashSet<DownloadRequest>();

    /** 需要进行网络访问的请求队列 **/
    private final PriorityBlockingQueue<DownloadRequest> mNetworkQueue = new PriorityBlockingQueue<DownloadRequest>();
    /** 需要进行本地访问的请求队列 **/
    private final PriorityBlockingQueue<DownloadRequest> mCacheQueue = new PriorityBlockingQueue<DownloadRequest>();

    /** 等待中的请求 **/
    private final Map<String, Queue<DownloadRequest>> mWaitingRequests = new HashMap<String, Queue<DownloadRequest>>();

    private final ExecutorDelivery mDelivery;

    private DownloadThread[] mDownloadThreads;
    private CacheThread[] mCacheThreads;

    private final BitmapCache mCache;

    private boolean mIsRunning = false;

    public ImageRequestQueue(BitmapCache cache) {
        mCache = cache;
        mDelivery = new ExecutorDelivery(new Handler(Looper.getMainLooper()));
        mDownloadThreads = new DownloadThread[DEFAULT_NETWORK_THREAD_POOL_SIZE];
        mCacheThreads = new CacheThread[DEFAULT_CACHE_THREAD_POOL_SIZE];
    }

    void start() {
        stop();

        for (int i = 0; i < mDownloadThreads.length; i++) {
            DownloadThread downloadThread = new DownloadThread();
            mDownloadThreads[i] = downloadThread;
            downloadThread.start();
        }

        for (int i = 0; i < mCacheThreads.length; i++) {
            CacheThread cacheThread = new CacheThread();
            mCacheThreads[i] = cacheThread;
            cacheThread.start();
        }

        mIsRunning = true;
    }

    void stop() {
        for (int i = 0; i < mDownloadThreads.length; i++) {
            if (mDownloadThreads[i] != null) {
                mDownloadThreads[i].quit();
            }
        }

        for (int i = 0; i < mCacheThreads.length; i++) {
            if (mCacheThreads[i] != null) {
                mCacheThreads[i].quit();
            }
        }

        mIsRunning = false;
    }

    public boolean isRunning() {
        return mIsRunning;
    }

    public void add(DownloadRequest request) {
        request.setQueue(this);

        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        request.setSequence(getSequenceNumber());

        // 相同的请求进入等待
        synchronized (mWaitingRequests) {
            String url = request.url;
            if (mWaitingRequests.containsKey(url)) {
                Queue<DownloadRequest> stagedRequests = mWaitingRequests.get(url);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<DownloadRequest>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(url, stagedRequests);
            } else {
                mWaitingRequests.put(url, null);
                mCacheQueue.add(request);
            }
        }
    }

    private int getSequenceNumber() {
        return mSequenceGenerator.incrementAndGet();
    }

    public void finish(DownloadRequest request) {
        synchronized (mCurrentRequests) {
            mCurrentRequests.remove(request);
        }

        synchronized (mWaitingRequests) {
            String url = request.url;
            Queue<DownloadRequest> waitingRequests = mWaitingRequests.remove(url);
            if (waitingRequests != null) {
                // 回调等待中的请求
                for (DownloadRequest waiting : waitingRequests) {
                    waiting.setBitmap(request.getBitmap());
                    onPostExecute(waiting);
                }
            }
        }
    }

    /**
     * 取消下载.如果url为空,那么代表取消所有
     * 
     * @param url
     */
    void cancel(String url) {
        synchronized (mCurrentRequests) {
            for (DownloadRequest request : mCurrentRequests) {
                if (TextUtils.isEmpty(url)) {
                    request.cancel();
                } else if (TextUtils.equals(url, request.url)) {
                    request.cancel();
                }
            }
        }
    }

    /**
     * 结束请求
     * 
     * @param request
     */
    private void onPostExecute(DownloadRequest request) {
        if (request == null || request.getBitmap() == null) {
            return;
        }
        Bitmap bitmap = request.getBitmap();

        // 添加到二级缓存
        mCache.addToMemoryCahce(request.url, bitmap);

        ImageDownloader.callback(request.listener, request.getBitmap(),
                bitmap == null ? ImageDownloadStateListener.LOAD_ERROR : ImageDownloadStateListener.LOAD_SUCCESS);
        ImageDownloader.setBitmapForView(request.view, bitmap, request.url);
    }

    /**
     * 从sd卡获取图
     * 
     * @author zhuqiao
     *
     */
    private class CacheThread extends Thread {
        private volatile boolean mQuit = false;

        public void quit() {
            mQuit = true;
            interrupt();
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            while (true) {
                DownloadRequest request;
                try {
                    request = mCacheQueue.take();

                    if (request.isCanceled()) {
                        continue;
                    }

                    ImageDownloader.callback(request.listener, null, ImageDownloadStateListener.LOADING);

                    // modify add 本地异步获取缩略图，复用imageloader 逻辑
                    Bitmap bitmap;
                    if (request.url.trim().startsWith("http")) {
                        bitmap = mCache.getBitmapFromDiscCache(request.url, request.config);
                    } else {
                        bitmap = mCache.getThumbnailFromDiscCache(request.url, request.config);
                    }

                    if (bitmap != null) {
                        request.setBitmap(bitmap);
                        mDelivery.post(request);
                    } else {
                        mNetworkQueue.add(request);
                    }
                } catch (InterruptedException e) {
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
            }
        }

    }

    /**
     * 从网络获取图片
     * 
     * @author zhuqiao
     *
     */
    private class DownloadThread extends Thread {
        private volatile boolean mQuit = false;

        public void quit() {
            mQuit = true;
            interrupt();
        }

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            while (true) {
                DownloadRequest request;
                try {
                    request = mNetworkQueue.take();

                    if (request.isCanceled()) {
                        continue;
                    }

                    ImageDownloader.callback(request.listener, null, ImageDownloadStateListener.LOADING);

                    // modify add 本地异步获取缩略图，复用imageloader 逻辑
                    Bitmap bitmap;
                    if (request.url.trim().startsWith("http")) {
                        bitmap = mCache.getBitmapFromNetwork(request.url, request.config);
                    } else {
                        bitmap = mCache.getThumbnailFromDiscCache(request.url, request.config);
                    }

                    request.setBitmap(bitmap);
                    mDelivery.post(request);
                } catch (InterruptedException e) {
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
            }
        }
    }

    private class ExecutorDelivery {
        private final Executor mResponsePoster;

        public ExecutorDelivery(final Handler handler) {
            mResponsePoster = new Executor() {

                @Override
                public void execute(Runnable command) {
                    handler.post(command);
                }
            };
        }

        public void post(DownloadRequest request) {
            mResponsePoster.execute(new ResponseDeliveryRunnable(request));
        }

        private class ResponseDeliveryRunnable implements Runnable {
            private final DownloadRequest mRequest;

            public ResponseDeliveryRunnable(DownloadRequest request) {
                mRequest = request;
            }

            @Override
            public void run() {
                if (mRequest == null) {
                    return;
                }

                onPostExecute(mRequest);

                mRequest.finish();
            }
        }

    }

}
